一般 Class 要使用非自身的物件時, 需在使用時 new 一個物件
# class-product.php
namespace App\Models;
use App\Models\Category;
Class Product {
public $category;
function __construct() {
echo 'Class Product 引入了';
# 在使用時 new 一個 Category
$this->category = new Category();
}
public function show() {
echo '顯示 Product';
echo ' 同時' ;
$this->category->show();
}
}
namespace App\Models;
Class Category {
function __construct() {
echo "Category Class 引入了";
}
public function show() {
echo '顯示 Category';
}
}
$p = new Product();
$p->show();
使用參數的方式引用需要的類別
namespace App\Models\DI;
Class Product {
public $category;
function __construct(Category $category) {
echo 'Class Product DI 引入了';
$this->category = $category;
}
public function show() {
echo '顯示 Product DI';
echo ' 同時' ;
$this->category->show();
}
}
namespace App\Models\DI;
Class Category {
function __construct() {
echo "Category DI Class 引入了";
}
public function show() {
echo '顯示 Category DI';
}
}
# 使用者自行 new 一個 Category
$category = new Category();
# 在 new Product 時隨參數傳入 Product 內
$p2 = new Product($category);
$p2->show();
使用 PHP Relfection 製作一個容器, 實現依賴注入(DI)與控制反轉(IoC)
namespace App\Kernel;
Class Container {
public static function autoClassRelfection($className, $params = []) {
$reflect = new \ReflectionClass($className);
// 取得構造函數
$construct = $reflect->getConstructor();
$args = [];
if ( $construct ) {
// 取得建構子的參數
$construct_params = $construct->getParameters();
foreach( $construct_params as $param ) {
$class = $param->getClass();
if ( $class ) {
$args[] = self::autoClassRelfection($class->name);
}
}
}
$args = array_merge($args, $params);
$instance = $reflect->newInstanceArgs($args);
return $instance;
}
}
$instance_product = Container::autoClassRelfection(Product::class);
$instance_product->show();
參考:
https://learnku.com/articles/56111
https://hackmd.io/@Daniel-Handsome/HJfzxORyF#%E6%87%89%E7%94%A8
GitHub 實作
https://github.com/harrison9824003/PHP
主要解析與綁定類別和介面的具體實例
依賴注入項目
app() 全域輔助函式(app() 為 Laravel 容器實例)
$logger = app('APP\ProductController');
$logger = app(Logger::class);
Laravel 綁定方式
# 設定在 Providider
public function register() {
# 開發者使用 app(Logger::class) 時就會回傳 Closure
$this->app->bind(Logger:class, function($app) {
return new Logger('log\path', 'error');
});
}
綁定 singleton、別名與實例
public function register() {
$this->app->singleton(Logger:class, function($app) {
return new Logger('log\path', 'error');
});
}
public function register() {
$logger = new Logger('log\path', 'error');
$this->app->instance(Logger::class, $logger);
}
# 1. 要求 Logger, 提供 CustomLog
$this->app->bind(Logger::class, CustomLog::class);
# 2. 要求 log, 提供CustomLog
$this->app->bind('log', CustomLog::class);
#3. 要求 log, 提供CustomLog
$this->app->alias(CustomLog::class, 'log');
# 建構式綁定 interface Class, 這樣可以自由使用繼承他的子類別
class Logger {
protected $mailer;
public function __construct(MailerInterface $mailer){
}
}
# 服務提供
# 假設 Mailer 與 MailerCustom 都是繼承 MailerInterface
public function register() {
# 此時可以使用 MailerSelf 或 MailerCustom 來發送郵件
# 只需要在 Provider 內調整
$this->app->bind(\Interfaces\Mailer::class, function() {
# return new MailerSelf();
return new MailerCustom();
}
}
# 依照指定條件給予相對的 Class
public function register() {
$this->app->when(Jobs\SendErrorEmail::class)
->nees(Interfaces\Mailer::class)
->give(Mailer\Self::class);
}
# 靜態呼叫
Log::alert('test');
# 等同於使用容器
$logger = app('log');
$logger->alert('test');
namespace Shop;
class Product {
public function editProduct() {
// ... 要執行的內容
}
}
# 綁定到容器上
App::bind('editproduct', function(){
return new \Shop\Product;
}
# 繼承 Illuminate\Support\Facades\Facade;
use Illuminate\Support\Facades\Facade;
# 定義 getFacadeAccessor() 方法
class Payment extend Facade {
protected static function getFacadeAccessor() {
# 回傳剛綁定在容器上的 Class
return 'editproduct';
}
}
# 實際運用
Product::editProduct();
# 或是
app('editproduct')->editProduct();